package cn.ahaoweb.drule.core.compile;

import cn.ahaoweb.drule.core.compile.enums.ConditionRelated;
import cn.ahaoweb.drule.core.compile.exception.DruleCompileException;
import cn.ahaoweb.drule.core.compile.structure.DruleBlock;
import cn.ahaoweb.drule.core.compile.structure.DruleCompileStructure;
import cn.ahaoweb.drule.core.compile.structure.DruleFunction;
import cn.ahaoweb.drule.core.model.enums.NodeType;
import cn.ahaoweb.drule.core.model.info.TreeNode;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

import static cn.ahaoweb.drule.core.compile.constant.CompileSymbols.*;

/**
 * @Name: 默认编译器
 * @Description: 将结构化规则对象编译为drl文件
 * @Author: wangzhonghao
 * @Date: 2025/2/7 14:54
 */
@Slf4j
public class DefaultDruleCompile {

    /**
     * 规则集合
     */
    protected List<DruleBlock> rules;

    /**
     * 入参对象
     */
    protected TreeNode input;

    /**
     * 出参对象
     */
    protected TreeNode output;

    /**
     * 引入
     */
    protected List<String> imports;


    /**
     * 全局变量 k->变量名，v->变量类型
     */
    protected Map<String, String> globals;

    /**
     * 函数集合
     */
    protected List<DruleFunction> functions;

    /**
     * 构建编译器
     *
     * @param rules       规则块集合
     * @param inputClass  入参类型
     * @param outputClass 出参类型
     */
    public DefaultDruleCompile(List<DruleBlock> rules, Class inputClass, Class outputClass) {
        this(rules, TreeNode.treeNode(inputClass), TreeNode.treeNode(outputClass));
    }

    /**
     * 构建编译器
     *
     * @param rules  规则块集合
     * @param input  入参类型的树型结构
     * @param output 出参类型的树型结构
     */
    public DefaultDruleCompile(List<DruleBlock> rules, TreeNode input, TreeNode output) {
        this.rules = rules;
        this.input = input;
        this.output = output;
        this.imports = new ArrayList<>();
        this.functions = new ArrayList<>();
        this.globals = new HashMap<>();
    }

    /**
     * 添加函数
     * <pre>
     *     注意函数名称为函数的唯一标识符，相同名称的函数只会存在一个（不支持覆盖）。
     * </pre>
     *
     * @param function
     * @return 是否添加成功
     */
    public boolean addFunction(DruleFunction function) {
        if (Objects.isNull(function)) {
            return false;
        }
        if (functions != null) {
            boolean exists = false;
            for (DruleFunction druleFunction : functions) {
                if (Objects.equals(druleFunction.getName(), function.getName())) {
                    exists = true;
                }
            }
            if (!exists) {
                functions.add(function);
                return true;
            }
        } else {
            functions = new ArrayList<>();
            functions.add(function);
            return true;
        }
        return false;
    }

    /**
     * 添加函数集合
     *
     * @param functions
     */
    public void addFunctions(Collection<DruleFunction> functions) {
        if (functions != null && functions.size() > 0) {
            for (DruleFunction function : functions) {
                addFunction(function);
            }
        }
    }

    /**
     * 引入包/类
     *
     * @param im
     */
    public void addImport(String im) {
        if (!imports.contains(im)) {
            imports.add(im);
        }
    }

    /**
     * 全局变量
     *
     * @param identify 标识符（变量名）
     * @param type     类型
     */
    public void addGlobal(String identify, String type) {
        if (identify != null) {
            globals.put(identify, type);
        }
    }

    public DruleCompileStructure compile() {
        DruleCompileStructure druleCompileStructure = new DruleCompileStructure();
        // 编译规则
        List<String> ruleContents = new ArrayList<>();
        for (DruleBlock rule : rules) {
            ruleContents.add(compileRule(rule));
        }
        druleCompileStructure.setImports(this.imports);
        druleCompileStructure.setGlobals(this.globals);
        druleCompileStructure.setRules(ruleContents);
        druleCompileStructure.setFunctions(new ArrayList<>());
        return druleCompileStructure;
    }

    /**
     * 编译规则
     *
     * @param druleBlock
     * @return
     */
    public String compileRule(DruleBlock druleBlock) {
        StringBuilder content = new StringBuilder();
        // 规则名称处理
        content.append("rule").append(blank).append(double_quotation_mark)
                .append(druleBlock.getRuleName()).append(double_quotation_mark).append(lf);
        if (druleBlock.getEnabled() != null) {
            content.append("enabled").append(blank).append(druleBlock.getEnabled()).append(lf);
        } else {
            content.append("enabled").append(blank).append("true").append(lf);
        }
        if (isNotBlank(druleBlock.getActivationGroup())) {
            content.append("activation-group").append(blank).append(double_quotation_mark)
                    .append(druleBlock.getActivationGroup()).append(double_quotation_mark).append(lf);
        }
        content.append("when").append(lf);
        content.append(compileWhen(druleBlock.getWhen()));

        content.append("then").append(lf);
        content.append(compileThen(druleBlock.getThen()));

        content.append("end").append(lf);
        log.debug("编译规则结束：\n{}", content);
        return content.toString();
    }

    private static boolean isNotBlank(String str) {
        return str != null && str.trim().length() > 0;
    }

    /**
     * 编译规则的条件部分
     *
     * @param when
     * @return
     */
    protected String compileWhen(DruleBlock.When when) {
        // 编译后的内容
        StringBuilder whenContent = new StringBuilder();
        if (when != null && when.getGroups() != null) {
            // 外层关联关系
            ConditionRelated outerRelated = when.getRelated();
            // 条件组集合
            List<DruleBlock.ConditionGroup> groups = when.getGroups();
            // 入参对象的code
            String root = input.getInfo().getCode();
            String fqcn = input.getInfo().getFqcn();
            addImport(fqcn);
            // 入参的子对象的code
            List<String> children = new ArrayList<>();
            // 对象对应的条件
            Map<String, List<DruleBlock.ConditionGroup>> objConditions = new HashMap<>();
            // 对象层次划分
            for (DruleBlock.ConditionGroup conditionGroup : groups) {
                List<DruleBlock.Condition> conditions = conditionGroup.getConditions();
                if (conditions != null && conditions.size() > 0) {
                    String p = null;
                    // 判断同一组条件的字段是否属于同一个对象下
                    for (DruleBlock.Condition condition : conditions) {
                        String path = condition.getFieldInfo().getPath();
                        String c = !path.contains(point) ? root : path.substring(0, path.lastIndexOf(point));
                        if (p == null) {
                            p = c;
                        } else {
                            if (!p.equals(c)) {
                                throw new DruleCompileException("同一组条件的参数字段必须归属于同一个对象。条件异常：" + condition);
                            }
                        }
                    }
                    if (!objConditions.containsKey(p)) {
                        objConditions.put(p, new ArrayList<>());
                    }
                    objConditions.get(p).add(conditionGroup);
                    if (!root.equals(p) && !children.contains(p)) {
                        children.add(p);
                    }
                }
            }
            // 先处理入参
            String simpleClassName = fqcn.substring(fqcn.lastIndexOf(point) + 1);
            if (objConditions.containsKey(root)) {
                whenContent.append(tab).append(root).append(colon).append(blank)
                        .append(simpleClassName).append(left_bracket).append(lf);
                // 获取条件
                List<DruleBlock.ConditionGroup> group = objConditions.get(root);
                processWhenCondition(whenContent, outerRelated, group);
                whenContent.append(tab).append(right_bracket).append(lf);
            } else {
                whenContent.append(tab).append(root).append(colon).append(blank)
                        .append(simpleClassName).append(left_bracket).append(right_bracket).append(lf);
            }
            // 处理入参的子对象
            if (children.size() > 0) {
                for (String child : children) {
                    TreeNode cn = input.find(child);
                    String cfqcn = cn.getInfo().getFqcn();
                    addImport(cfqcn);
                    String className = cfqcn.substring(cfqcn.lastIndexOf(point) + 1);
                    whenContent.append(tab).append(cn.getInfo().getCode()).append(colon).append(blank)
                            .append(className).append(left_bracket).append(lf);
                    List<DruleBlock.ConditionGroup> group = objConditions.get(child);
                    processWhenCondition(whenContent, outerRelated, group);
                    whenContent.append(tab).append(right_bracket).append(blank).append("from").append(blank)
                            .append(child).append(lf);
                }
            }
        } else {
            whenContent.append(tab).append("eval(true)").append(lf);
        }
        return whenContent.toString();
    }

    /**
     * 编译条件组集合
     *
     * @param whenContent  条件内容
     * @param outerRelated 外部关联关系
     * @param group        条件组集合
     */
    protected void processWhenCondition(StringBuilder whenContent, ConditionRelated outerRelated, List<DruleBlock.ConditionGroup> group) {
        boolean prefix = false;
        for (DruleBlock.ConditionGroup conditionGroup : group) {
            // 内层条件关联关系
            ConditionRelated related = conditionGroup.getRelated();
            List<DruleBlock.Condition> conditions = conditionGroup.getConditions();
            String content = "";
            for (DruleBlock.Condition condition : conditions) {
                TreeNode treeNode = input.find(condition.getFieldInfo().getPath());
                if (treeNode != null) {
                    condition.getFieldInfo().setKind(treeNode.getType());
                } else {
                    // 识别字段数据类型
                    String dataType = condition.getFieldInfo().getDataType();
                    try {
                        Class<?> aClass = Class.forName(dataType);
                        condition.getFieldInfo().setKind(NodeType.type(aClass,null));
                    } catch (ClassNotFoundException e) {
                    }
                }
                // 编译语句
                String compile = condition.getOp().op().compile(condition);
                if (content.length() > 0) {
                    content = content + blank + related.getSymbol() + blank + compile;
                } else {
                    content = compile;
                }
            }
            if (conditions.size() > 1) {
                content = left_bracket + blank + content + blank +  right_bracket;
            }
            if (prefix) {
                whenContent.append(tab2).append(outerRelated.getSymbol()).append(blank).append(content).append(lf);
            } else {
                whenContent.append(tab2).append(content).append(lf);
            }
            prefix = true;
        }
    }

    /**
     * 编译规则结果部分
     *
     * @param then
     * @return
     */
    protected String compileThen(DruleBlock.Then then) {
        if (then == null){
            return "";
        }
        // 编译后的内容
        StringBuilder thenContent = new StringBuilder();
        List<DruleBlock.Action> actions = then.getActions();
        if (actions != null && actions.size() > 0) {
            String code = output.getInfo().getCode();
            addImport(output.getInfo().getFqcn());
            addGlobal(output.getInfo().getCode(), output.getInfo().getFqcn());
            for (DruleBlock.Action action : actions) {
                String fieldCode = action.getFieldInfo().getFieldCode();
                thenContent.append(tab)
                        .append(code)
                        .append(point)
                        .append("set").append(upFirst(fieldCode))
                        .append(left_bracket)
                        .append(processActionValue(action))
                        .append(right_bracket)
                        .append(semicolon)
                        .append(lf);
            }
        }
        return thenContent.toString();
    }

    public static String processActionValue(DruleBlock.Action action) {
        return String.class.getCanonicalName().equals(action.getFieldInfo().getDataType()) ?
                Operator.preprocessString(action.getValue()) : Operator.preprocess(action.getValue());
    }

    protected static String upFirst(String s) {
        if (s != null && s.length() > 0) {
            return Character.toUpperCase(s.charAt(0)) + s.substring(1);
        } else {
            return s;
        }
    }

    /**
     * 效验 规则结构
     *
     * @return
     */
    public ValidateResult validateRules() {
        ValidateResult validateResult = new ValidateResult();
        if (this.rules == null) {
            validateResult.setCode(ValidateResult.ERROR);
            validateResult.addMessage("规则块列表为空", "null");
            return validateResult;
        }
        for (int i = 0; i < this.rules.size(); i++) {
            DruleBlock rule = this.rules.get(i);
            if (rule == null) {
                validateResult.addMessage("存在空规则块，位置", String.valueOf(i + 1));
            }
            ValidateResult.Message message = new ValidateResult.Message("规则块【名称：" + rule.getRuleName() + "】出现异常", new ArrayList<>());
            message.setSeq("");
            if (!isNotBlank(rule.getRuleName())) {
                message.add("\n  规则名称为空");
            }
            DruleBlock.When when = rule.getWhen();
            if ( when != null && when.getGroups() != null && !when.getGroups().isEmpty()) {
                if (when.getRelated() == null) {
                    message.add("\n  外层条件关联为空");
                }
                if (when.getGroups() != null) {
                    for (int j = 0; j < when.getGroups().size(); j++) {
                        DruleBlock.ConditionGroup conditionGroup = when.getGroups().get(j);
                        int groupNumber = j + 1;
                        if (conditionGroup == null) {
                            message.add("\n  条件组#" + groupNumber + "为空");
                        } else {
                            if (conditionGroup.getRelated() == null) {
                                message.add("\n  条件组#" + groupNumber + "的内层条件关联为空");
                            }
                            if (conditionGroup.getConditions() != null) {
                                for (int k = 0; k < conditionGroup.getConditions().size(); k++) {
                                    int conditionNumber = k + 1;
                                    DruleBlock.Condition condition = conditionGroup.getConditions().get(k);
                                    if (condition == null) {
                                        message.add("\n  条件组#" + groupNumber + "存在空条件");
                                    } else {
                                        String con = "";
                                        boolean conError = false;
                                        if (condition.getFieldInfo() == null) {
                                            con += "参数信息为空";
                                            conError=true;
                                        } else {
                                            DruleBlock.RuleFieldInfo fieldInfo = condition.getFieldInfo();
                                            if (fieldInfo.getPath() == null
                                                    || fieldInfo.getFieldCode() == null
                                                    || fieldInfo.getDataType() == null) {
                                                con += "参数信息异常";
                                                conError=true;
                                            }
                                        }
                                        if (condition.getOp() == null) {
                                            if (!"".equals(con)){
                                                con += ",";
                                            }
                                            con += "操作符为空";
                                            conError=true;
                                        }
                                        if (condition.getJudgeValue() == null) {
                                            if (!"".equals(con)){
                                                con += ",";
                                            }
                                            con += "判断值为空";
                                            conError=true;
                                        }
                                        if (conError){
                                            message.add("\n  条件组#" + groupNumber + "的第" + conditionNumber + "个条件：" + con);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            if (rule.getThen() != null) {
                List<DruleBlock.Action> actions = rule.getThen().getActions();
                if (actions != null) {
                    for (int j = 0; j < actions.size(); j++) {
                        DruleBlock.Action action = actions.get(j);
                        String s = "";
                        if (action.getFieldInfo() == null){
                            s += "参数为空";
                        }
                        if (action.getValue() == null){
                            if (!"".equals(s)){
                                s += ",";
                            }
                            s += "赋值为空";
                        }
                        if (s.length() > 0){
                            message.add("\n  结果#" + (j + 1) + "出现异常："+s);
                        }
                    }
                }
            }
            if (!message.getContent().isEmpty()) {
                validateResult.getMessages().add(message);
            }
        }
        if (validateResult.getMessages().isEmpty()) {
            return ValidateResult.success();
        } else {
            return validateResult;
        }
    }

}
